"""图片下载器"""

import argparse
import csv
import logging
import os
import re
import sys
from logging import error, exception, info, warning
from comutils import (
    COMMON_SEP_PATTERN,
    URL_SEP_PATTERN,
    URL_SEPARATORS,
    get_data_from_csv,
    # split_multi,
)
from imgdown import ImageDownloader, USER_AGENTS, logger
from filenamehandler import FileNameHandler as fh
from wooenums import CSVProductFields

csv.field_size_limit(int(1e7))  # 允许csv文件最大为10MB
# 或者根据实际类定义位置调整导入路径
IMG_DIR = "./images"


def parse_image_sources(file, args, lines):
    """
    解析输入文件，提取图片下载所需的URL和文件名。

    :param file_path: 输入文件路径
    :param args: 命令行参数对象（包含 from_specific_csv, from_csv 等标志）
    :param lines: 用于存储结果的列表(多次调用将会累加到这个列表中)，格式为 [(name, url), ...] 或 [url, ...]
    :return: 成功返回 True，失败抛出异常或返回 False
    """
    try:
        with open(file, "r", encoding="utf-8") as f:
            if args.from_specific_csv:
                reader = csv.DictReader(f)
                name_field = CSVProductFields.IMAGES.value
                url_field = CSVProductFields.IMAGES_URL.value
                get_data_from_csv(args, lines, reader, url_field, name_field)

            elif args.from_csv:
                reader = csv.DictReader(f)
                reader_headers = reader.fieldnames or []
                # 打印出csv文件中所有字段名
                for idx, header in enumerate(reader_headers):
                    print(f"{idx}. {header}")

                selected_ids = input(
                    "请输入需要下载的图片的列号,如果只有一列,则视为图片url列,"
                    "如果指定两列,则第一列视为文件名,第二列为URL,输入的列号间用空格/逗号隔开: "
                )
                res = re.split(COMMON_SEP_PATTERN, selected_ids.strip())
                args.info(f"You selected: {res}")

                if len(res) == 2:
                    # 分别解析字段索引,然后获取字段名
                    name_field_id, url_field_id = res
                    name_field = reader_headers[int(name_field_id)]
                    url_field = reader_headers[int(url_field_id)]
                elif len(res) == 1:
                    url_field = reader_headers[int(res[0])]
                    name_field = ""
                else:
                    raise ValueError("请按照正确的格式输入列号")

                get_data_from_csv(args, lines, reader, url_field, name_field)

            else:
                for line in f:
                    parts = re.split(pattern=URL_SEP_PATTERN, string=line.strip())
                    if args.name_url_pairs:
                        if len(parts) == 2:
                            name, url = parts
                            lines.append((name.strip(), url.strip()))
                        else:
                            warning(f"无效的行格式: {line}; parts={parts}")
                    else:
                        lines.append(line.strip())

                if not lines:
                    error("没有有效的文件名和URL对")
                    return False

        return True

    except Exception as e:
        error(f"读取输入文件失败: {str(e)}")
        return False


def parse_arguments_for_imgdown():
    """解析命令行参数

    利用parser.add_argument()方法添加命令行参数，并解析命令行参数
    长选项--开头,例如--workers,指出参数将会绑定到相应的变量上,经过parse_args()解析,将构造对应的参数包
    """
    parser = argparse.ArgumentParser(description="多线程图片下载器")
    parser.add_argument(
        "-i",
        "--test-url",
        help="手动指定若干图片URL进行测试下载,如果有多个URL,用空格分隔",
    )
    parser.add_argument(
        "-f",
        "--file-input",
        nargs="+",
        required=False,
        help="包含图片URL的输入[文件],允许指定多个文件",
    )
    parser.add_argument(
        "-a",
        "--format-consistent",
        action="store_true",
        help="所有输入文件是否为相同的格式(如果是,可以避免多次询问文件格式)",
    )
    parser.add_argument(
        "-d",
        "--dir-input",
        nargs="+",
        required=False,
        help="包含图片URL的输入[目录]，允许指定多个目录(todo)",
    )
    parser.add_argument(
        "-c",
        "--from-specific-csv",
        action="store_true",  # 开关式参数
        help="指定的文件是特定csv文件(df团队定制格式),从csv文件中指定的图片名字/链接下载",
    )
    parser.add_argument(
        "-C",
        "--from-csv",
        action="store_true",  # 开关式参数
        help="指定的文件是普通csv文件,从csv文件中下载所有图片",
    )
    parser.add_argument(
        "-n",
        "--name-url-pairs",
        action="store_true",  # 开关式参数
        help=f'输入文件包含文件名和URL对，格式为"文件名 URL"，以[{URL_SEPARATORS}]中指定的符号分隔',
    )
    parser.add_argument(
        "-o", "--output-dir", default=IMG_DIR, help="图片保存目录 (默认: ./images)"
    )
    parser.add_argument(
        "-w", "--workers", type=int, default=10, help="下载线程数 (默认: 10)"
    )
    parser.add_argument(
        "-t", "--timeout", type=int, default=30, help="下载超时时间，单位秒 (默认: 30)"
    )
    parser.add_argument(
        "-r", "--retry", type=int, default=2, help="下载失败重试次数 (默认: 1)"
    )
    parser.add_argument(
        "-u", "--user-agent", default=USER_AGENTS[0], help="自定义User-Agent"
    )
    parser.add_argument(
        "-s",
        "--verify-ssl",
        action="store_true",
        help="是否验证SSL证书(启用会提高安全性，但可能降低下载速度以及成功率)",
    )
    parser.add_argument("--proxy-file", help="代理IP地址列表文件路径")
    parser.add_argument("--cookie-file", help="包含Cookies的JSON文件路径")

    parser.add_argument("-v", "--verbose", action="store_true", help="显示详细日志")

    return parser.parse_args()


def main():
    """主函数"""

    # 解析命令行用户传输进来的参数,像字典一样使用它
    args = parse_arguments_for_imgdown()

    # 设置日志级别
    if args.verbose:
        logger.setLevel(logging.DEBUG)

    # 读取输入文件
    lines = []
    if args.test_url:
        # 处理测试URL下载
        lines = re.split(URL_SEP_PATTERN, args.test_url)
    elif args.file_input:
        # 处理文件输入
        files = args.file_input
        # files = split_multi(files)
        for file in files:
            parse_image_sources(file=file, args=args, lines=lines)
    elif args.dir_input:
        # 处理目录输入
        dirs = args.dir_input
        # dirs = split_multi(dirs)
        for d in dirs:
            for file in os.listdir(d):
                parse_image_sources(file=file, args=args, lines=lines)

    # 创建下载器
    downloader = ImageDownloader(
        max_workers=args.workers,
        timeout=args.timeout,
        retry_times=args.retry,
        user_agent=args.user_agent,
    )
    # 过滤已有图片,扫描出尚未下载的图片
    # 这里不关心文件名后缀的差异,比较basename
    ## 读取指定目录下的图片(只列出名字)
    if not os.path.exists(args.output_dir):
        warning(
            f"指定的输出目录{args.output_dir}不存在: {args.output_dir}(将尝试自动创建)"
        )
    else:
        # 如果指定的存放目录存在
        img_names_existed = os.listdir(args.output_dir)
        img_names_existed = [os.path.splitext(name)[0] for name in img_names_existed]
        # 记录过滤前的待下载图片数量
        total_num_raw = len(lines)
        if args.name_url_pairs:
            # 从二元组中解析出名字
            lines = [
                (name, _)
                for name, _ in lines
                if fh.get_filebasename_from_url_or_path(name) not in img_names_existed
            ]
        else:
            # 从URL列表中解析出名字
            lines = [
                url
                for url in lines
                if fh.get_filebasename_from_url_or_path(url) not in img_names_existed
            ]
        total_num_filtered = len(lines)
        # 统计多少图片被过滤掉
        num_filtered = total_num_raw - total_num_filtered
        info(
            f"过滤掉{num_filtered}张图片(过滤前后分别有: {total_num_raw}张, {total_num_filtered}张)"
        )

    # 下载图片
    ## 是否使用自定义文件名
    if args.name_url_pairs:
        # 解析文件名和URL对
        try:
            downloader.download_with_names(
                name_url_pairs=lines, output_dir=args.output_dir
            )
        except Exception as e:
            exception(f"下载过程中发生错误: {str(e)}")
            return 1
    else:
        # 直接下载URL列表中的图片
        try:
            if not lines:
                error("没有有效的URL")
                return 1

            downloader.download(lines, args.output_dir)
        except Exception as e:
            exception(f"下载过程中发生错误: {str(e)}")
            return 1

    return 0


if __name__ == "__main__":
    info("welcome to use image downloader!")
    sys.exit(main())
